﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace Utilities.Helper
{
    public static class PropertyHelperExtensions
    {
        public static string GetMemberName<T>(
            this T instance,
            Expression<Func<T, object>> expression)
        {
            return GetMemberName(expression);
        }

        public static string GetMemberName<T>(
            Expression<Func<T, object>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        public static string GetMemberName<T>(
            this T instance,
            Expression<Action<T>> expression)
        {
            return GetMemberName(expression);
        }

        public static string GetMemberName<T>(
            Expression<Action<T>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            return GetMemberName(expression.Body);
        }

        private static string GetMemberName(
            Expression expression)
        {
            if (expression == null)
            {
                throw new ArgumentException(
                    "The expression cannot be null.");
            }

            if (expression is MemberExpression)
            {
                // Reference type property or field
                var memberExpression =
                    (MemberExpression)expression;
                return memberExpression.Member.Name;
            }

            if (expression is MethodCallExpression)
            {
                // Reference type method
                var methodCallExpression =
                    (MethodCallExpression)expression;
                return methodCallExpression.Method.Name;
            }

            if (expression is UnaryExpression)
            {
                // Property, field of method returning value type
                var unaryExpression = (UnaryExpression)expression;
                return GetMemberName(unaryExpression);
            }

            throw new ArgumentException("Invalid expression");
        }

        private static string GetMemberName(
            UnaryExpression unaryExpression)
        {
            if (unaryExpression.Operand is MethodCallExpression)
            {
                var methodExpression =
                    (MethodCallExpression)unaryExpression.Operand;
                return methodExpression.Method.Name;
            }

            return ((MemberExpression)unaryExpression.Operand)
                .Member.Name;
        }

        public static void CreateFieldExpression<T>(T theinstance, Expression<Func<T, object>> expression)
        {
            // Create a MemberExpression that represents getting
            // the value of the 'species' field of class 'Animal'.
            System.Linq.Expressions.MemberExpression memberExpression =
                System.Linq.Expressions.Expression.Field(
                    System.Linq.Expressions.Expression.Constant(theinstance),
                     GetMemberName(expression));
        }


        public static object GetValue(MemberExpression expression)
        {
            var constantSelector = (ConstantExpression)expression.Expression;
            object value = ((FieldInfo)expression.Member).GetValue(constantSelector.Value);

            if (value == null)
            {
                string name = expression.Member.Name;
                throw new ArgumentNullException(name);
            }
            return value;
        }


        public static object GetValue<T>(Expression<Func<T>> selector)
        {
            return GetValue((MemberExpression)selector.Body);

            //var memberSelector = (MemberExpression)selector.Body;
            //var constantSelector = (ConstantExpression)memberSelector.Expression;
            //object value = ((FieldInfo)memberSelector.Member)
            //    .GetValue(constantSelector.Value);

            //if (value == null)
            //{ 
            //    string name = ((MemberExpression)selector.Body).Member.Name;
            //    throw new ArgumentNullException(name);
            //}

            //return value;
        }



        public static object CompileGetValue(MemberExpression member)
        {
            var objectMember = Expression.Convert(member, typeof(object));

            var getterLambda = Expression.Lambda<Func<object>>(objectMember);

            var getter = getterLambda.Compile();

            return getter();
        }

        public static object CompileGetValue<T>(Expression<Func<T>> selector)
        {
            return CompileGetValue((MemberExpression)selector.Body);

            //var objectMember = Expression.Convert(member, typeof(object));

            //var getterLambda = Expression.Lambda<Func<object>>(objectMember);

            //var getter = getterLambda.Compile();

            //return getter();
        }



        public static object CompileGetValueOfProperty(string propName, object instance)
        {
            System.Linq.Expressions.MemberExpression memberExpression_This =
                       System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression.Constant(instance),
                                                                   propName);

            return  PropertyHelperExtensions.CompileGetValue(memberExpression_This);

        }

        public static void CompileSetValueOfProperty(object instance, string propName, Type propType, object value)
        {
            System.Linq.Expressions.MemberExpression memberExpression_This =
                       System.Linq.Expressions.Expression.Property(System.Linq.Expressions.Expression.Constant(instance),
                                                                   propName);

         

           // ParameterExpression ValueExp = Expression.Parameter(typeof(object), "Value");
           //var memberExpression_This_as_Object = Expression.Convert(memberExpression_This, typeof(object));
           //BinaryExpression AssignExp = Expression.Assign(memberExpression_This_as_Object, ValueExp);
           //var setter = Expression.Lambda<Action<object>>(AssignExp, ValueExp).Compile();

            ParameterExpression ValueExp = Expression.Parameter(propType, "Value");
            var ValueExp_as_Typeof = Expression.Convert(ValueExp, propType);
            BinaryExpression AssignExp = Expression.Assign(memberExpression_This, ValueExp);
            var setter = Expression.Lambda<Action<object>>(AssignExp, ValueExp).Compile();
          

            setter(value);

        }


    }
}